package com.definity.toolkit.ui.action;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.swing.SwingWorker;
import javax.swing.SwingWorker.StateValue;

public abstract class ActionProcess<T> implements PropertyChangeListener {

	private final List<ProcessProgressListener> listeners = new ArrayList<ProcessProgressListener>();
	private ProcessManager processManager;
	private SwingWorker<T, Void> worker;

	public ActionProcess() {
		
	}

	public final void addProgressListener(ProcessProgressListener listener) {
		listeners.add(listener);
	}

	public final void removeProgressListener(ProcessProgressListener listener) {
		listeners.remove(listener);
	}
	
	public abstract String getName();
	
	protected abstract T run();

	protected void done() {
		
	}
	
	public String getDescription() {
		return null;
	}
	
	final void setWorker(ProcessManager processManager, SwingWorker<T, Void> worker) {
		if (this.worker != null)
			throw new ProcessException("Process worker already exists");
		
		worker.addPropertyChangeListener(this);

		this.worker = worker;
		this.processManager = processManager;
	}

	public final boolean isCancelled() {
		return worker.isCancelled();
	}

	public final boolean isDone() {
		return worker.isDone();
	}
	
	public final T get() throws InterruptedException, ExecutionException {
		return worker.get();
	}

	public final T get(long timeout, TimeUnit unit)
			throws InterruptedException, ExecutionException, TimeoutException {
		return worker.get(timeout, unit);
	}

	public final StateValue getState() {
		return worker.getState();
	}

	public final int getProgress() {
		return worker.getProgress();
	}

	final void execute() {
		if (worker == null)
			throw new ProcessException("Process " + getName() + " isn't working");
		worker.execute();
	}
	
	public final void kill() {
		if (worker == null)
			throw new ProcessException("Process " + getName() + " isn't working");
		worker.cancel(true);
		processManager.removeProcess(this);
	}

	@Override
	public final void propertyChange(PropertyChangeEvent evt) {
		if ("progress".equals(evt.getPropertyName())) {
			fireProgressListeners((Integer) evt.getNewValue());
		}
	}

	private void fireProgressListeners(Integer progress) {
		for (ProcessProgressListener listener : listeners) {
			listener.change(getName(), getProgress());
		}
	}

	public final void destroy() {
		worker = null;
		processManager = null;
	}
	
}
